package internal

import (
	"context"
	"fmt"
	"gitee.com/wanjimao/dongli-kit/pkg"
	errors2 "gitee.com/wanjimao/dongli-platform/services/profile/errors"
	"gitee.com/wanjimao/dongli-util/dancetiktok"
	"gitee.com/wanjimao/dongli-util/dancetiktok/model/dancetiktokmodelmysql"
	"gitee.com/wanjimao/dongli-util/helper"
	"gorm.io/gorm"
	"time"

	"gitee.com/wanjimao/dongli-kit/orm/nerrors"
	"github.com/pkg/errors"
	"go.uber.org/zap"

	pb "gitee.com/wanjimao/dongli-platform/api/gen/auth/api"
	"gitee.com/wanjimao/dongli-platform/services/auth/internal/db/redis"
	"gitee.com/wanjimao/dongli-platform/services/auth/internal/utils"
)

func (s *Service) Authenticate(_ context.Context, request *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error) {
	logInfo := pkg.LogInfo{
		Method:     "Authenticate",
		TimeString: time.Now().Format(time.DateTime),
		TimeUnix:   time.Now().Unix(),
		UserId:     0,
		Req:        helper.InterfaceHelperObject.ToString(request),
		LogId:      request.LogId,
	}
	//查询用户角色数据
	resp := &pb.AuthenticateResponse{}
	//用户
	var accountRole dancetiktokmodelmysql.DanceUserAccountRole
	tx := s.mysqlDb.MysqlDanceDb.Model(&dancetiktokmodelmysql.DanceUserAccountRole{}).Where("id = ?", request.Id).First(&accountRole)
	if nil != tx.Error && gorm.ErrRecordNotFound != tx.Error {
		s.logger.Error(fmt.Sprintf("查询角色异常 logInfo:%+v err:%+v", logInfo, tx.Error))
		resp.ErrCode = dancetiktok.ErrorLoginDataException
		return resp, errors2.ErrLoadFailure
	}
	if accountRole.Id == 0 || accountRole.AccountId == 0 {
		s.logger.Error(fmt.Sprintf("取出的数据异常 logInfo:%+v err:%+v", logInfo, tx.Error))
		resp.ErrCode = dancetiktok.ErrorLoginDataException
		return resp, errors2.ErrNotFound
	}

	//id := data.GetUid()
	if isBlocked, err := redis.IsBlocked(s.redisCli, request.Id); err != nil {
		s.logger.Error("check and unblock profile failed", zap.Error(err))
		s.logger.Error(fmt.Sprintf("黑名单不能登录 logInfo:%+v err:%+v", logInfo, nil))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
		//return nil, ErrGeneralFailure
	} else if isBlocked {
		s.logger.Info("account is blocked", zap.String("uid", request.Id))
		return &pb.AuthenticateResponse{
			Uid: request.Id,
		}, ErrPermissionDenied
	}

	isOverride := false
	if access, err := utils.CreatJwt(request.Id, utils.TokenTypeAccess, s.jwtSecret, nil, s.jwtExpire); err != nil {
		s.logger.Error(fmt.Sprintf("generate access jwt failed logInfo:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else if isOverride, err = redis.IsAuthTokenExist(s.redisCli, request.Id); err != nil {
		s.logger.Error(fmt.Sprintf("get auth token failed:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else if err := redis.SaveAuthToken(s.redisCli, request.Id, access, s.jwtExpire); err != nil {
		s.logger.Error(fmt.Sprintf("save access token failed:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else if refresh, err := utils.CreatJwt(request.Id, utils.TokenTypeRefresh, s.jwtSecret, nil, s.jwtExpire); err != nil {
		s.logger.Error(fmt.Sprintf("generate jwt refresh token failed:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else {
		return &pb.AuthenticateResponse{
			AccessToken:  access,
			RefreshToken: refresh,
			Uid:          request.Id,
			IsOverride:   isOverride,
		}, nil
	}
}

func (s *Service) ValidateToken(_ context.Context, request *pb.ValidateTokenRequest) (*pb.ValidateTokenResponse, error) {
	logInfo := pkg.LogInfo{
		Method:     "ValidateToken",
		TimeString: time.Now().Format(time.DateTime),
		TimeUnix:   time.Now().Unix(),
		UserId:     0,
		Req:        helper.InterfaceHelperObject.ToString(request),
		LogId:      request.LogId,
	}
	resp := &pb.ValidateTokenResponse{}
	if request.AccessToken == "" {
		//s.logger.Error("access token is empty")
		//return nil, ErrClientParamFailure
		s.logger.Error(fmt.Sprintf("access token is empty:%+v err:%+v", logInfo, nil))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else if uid, data, err := utils.ParseToken(request.AccessToken, utils.TokenTypeAccess, s.jwtSecret); err != nil {
		//s.logger.Error("parse jwt token failed", zap.Error(err))
		//return nil, ErrParseJwtTokenFailure
		s.logger.Error(fmt.Sprintf("parse jwt token failed:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else if isExist, err := redis.IsAuthTokenSame(s.redisCli, uid, request.AccessToken); err != nil {
		//s.logger.Error("get auth token failed", zap.Error(err))
		//return nil, ErrGeneralFailure
		s.logger.Error(fmt.Sprintf("get auth token failed:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else if !isExist {
		return nil, ErrGeneralFailure
	} else if isBlocked, err := redis.IsBlocked(s.redisCli, uid); err != nil {
		//s.logger.Error("check and unblock profile failed", zap.Error(err))
		//return nil, ErrGeneralFailure
		s.logger.Error(fmt.Sprintf("check and unblock profile failed:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else if isBlocked {
		//s.logger.Info("profile is blocked", zap.String("uid", uid))
		//return nil, ErrPermissionDenied
		s.logger.Error(fmt.Sprintf("profile is blocked:%+v err:%+v", logInfo, err))
		resp.ErrCode = dancetiktok.NetworkAnomalyPleaseTryAgainLater
		return resp, errors2.ErrLoadFailure
	} else {
		return &pb.ValidateTokenResponse{
			Uid:        uid,
			CustomData: data,
		}, nil
	}
}

func (s *Service) RefreshToken(_ context.Context, request *pb.RefreshTokenRequest) (*pb.RefreshTokenResponse, error) {
	if uid, _, err := utils.ParseToken(request.RefreshToken, utils.TokenTypeRefresh, s.jwtSecret); err != nil {
		return nil, errors.Wrap(ErrParseJwtTokenFailure, err.Error())
	} else if access, err := utils.CreatJwt(uid, utils.TokenTypeAccess, s.jwtSecret, nil, s.jwtExpire); err != nil {
		return nil, errors.Wrap(ErrGenerateJwtFailure, err.Error())
	} else if refresh, err := utils.CreatJwt(uid, utils.TokenTypeRefresh, s.jwtSecret, nil, s.jwtExpire); err != nil {
		return nil, errors.Wrap(ErrGenerateJwtFailure, err.Error())
	} else {
		return &pb.RefreshTokenResponse{
			AccessToken:  access,
			RefreshToken: refresh,
		}, nil
	}
}

func (s *Service) ClearToken(_ context.Context, request *pb.ClearTokenRequest) (*pb.ClearTokenResponse, error) {
	if request.Uid == "" {
		s.logger.Error("uid is empty", zap.String("uid", request.Uid))
		return nil, ErrClientParamFailure
	} else if isSame, err := redis.IsAuthTokenSame(s.redisCli, request.Uid, request.AccessToken); err != nil {
		s.logger.Error("get auth token failed", zap.Error(err))
		return nil, ErrGeneralFailure
	} else if !isSame {
		return &pb.ClearTokenResponse{}, nil
	} else if err := redis.ClearAuthToken(s.redisCli, request.Uid); err != nil {
		s.logger.Error("clear auth token failed", zap.Error(err))
		return nil, ErrGeneralFailure
	}
	return &pb.ClearTokenResponse{}, nil
}

func (s *Service) Delete(_ context.Context, request *pb.DeleteRequest) (*pb.DeleteResponse, error) {
	var err error
	if request.Id == "" {
		s.logger.Error("uid is empty", zap.String("uid", request.Id))
		return nil, ErrClientParamFailure
	}
	if err = s.db.Delete(request.Id); err != nil {
		if errors.Is(err, nerrors.ErrNotFound) {
			return &pb.DeleteResponse{}, nil
		}
		return &pb.DeleteResponse{}, fmt.Errorf("db delete fail: %w", err)
	}
	return &pb.DeleteResponse{}, nil
}

func (s *Service) AddBlocked(_ context.Context, request *pb.AddBlockedRequest) (*pb.AddBlockedResponse, error) {
	if request.Uid == "" {
		s.logger.Error("uid is empty", zap.String("uid", request.Uid))
		return nil, ErrClientParamFailure
	}

	if request.GetIsBlock() {
		if err := redis.BlockedProfile(
			s.redisCli,
			request.Uid,
			time.Duration(request.GetDuration())*time.Second,
		); err != nil {
			s.logger.Error("block profile failed", zap.Error(err))
			return nil, ErrGeneralFailure
		}
	} else if err := redis.UnBlockedProfile(s.redisCli, request.Uid); err != nil {
		s.logger.Error("unblock profile failed", zap.Error(err))
		return nil, ErrGeneralFailure
	}
	return &pb.AddBlockedResponse{}, nil
}

func (s *Service) PackToken(_ context.Context, request *pb.PackTokenRequest) (*pb.PackTokenResponse, error) {
	if request.Uid == "" {
		s.logger.Error("uid is empty", zap.String("uid", request.Uid))
		return nil, ErrClientParamFailure
	}
	expire := 60 * time.Minute
	if request.Duration > 0 {
		expire = time.Duration(request.Duration) * time.Minute
	}
	token, err := utils.CreatJwt(request.Uid, utils.TokenTypeAccess, s.jwtSecret, request.GetCustomData(), expire)
	if err != nil {
		s.logger.Error("generate jwt token failed", zap.Error(err))
		return nil, ErrGenerateJwtFailure
	}
	return &pb.PackTokenResponse{
		AccessToken: token,
	}, nil

}
